import { createCipheriv, createDecipheriv, createHmac } from 'crypto';
import fs from 'fs-extra';
import { Block } from './block.js';
import { BLOCKS_PATH, HEAD_PATH, KEY } from './constants.js';

const { readFileSync, existsSync, writeFileSync, emptyDirSync, mkdirSync } = fs;

/**
 * DES 加密
 * @param {Buffer} rawData 原始数据
 * @param {string} key 密钥
 * @returns 加密后数据
 */
export function desEncrypt(rawData, key) {
  key =
    key.length >= 8 ? key.slice(0, 8) : key.concat('0'.repeat(8 - key.length));
  const keyHex = Buffer.from(key);
  const cipher = createCipheriv('des-cbc', keyHex, keyHex);
  return Buffer.concat([cipher.update(rawData), cipher.final()]);
}

/**
 * DES 解密
 * @param {Buffer} encryptedData 加密后的数据
 * @param {string} key 密钥
 * @returns 原始数据
 */
export function desDecrypt(encryptedData, key) {
  key =
    key.length >= 8 ? key.slice(0, 8) : key.concat('0'.repeat(8 - key.length));
  const keyHex = Buffer.from(key);
  const cipher = createDecipheriv('des-cbc', keyHex, keyHex);
  return Buffer.concat([cipher.update(encryptedData), cipher.final()]);
}

/**
 * 加密函数
 * @param {Buffer}} buffer 原始数据
 * @returns 加密后的数据
 */
export const encrypt = (rawData) => desEncrypt(rawData, KEY);

/**
 * 解密函数
 * @param {Buffer} encryptedData 加密后的数据
 * @returns 原始数据
 */
export const decrypt = (encryptedData) => desDecrypt(encryptedData, KEY);

/**
 * 获取数据的hash
 * @param {Buffer} secret 数据
 * @returns hash
 */
export const getHash = (secret) =>
  createHmac('sha256', secret).update('MinarBlockChain').digest('hex');

/**
 * 创建区块链
 * @param {Buffer}} genesisContent 创世区块内容
 * @returns 创世区块hash
 */
export function createChain(genesisContent) {
  existsSync(BLOCKS_PATH) || mkdirSync(BLOCKS_PATH);
  emptyDirSync(BLOCKS_PATH);
  const genesisBlock = new Block(genesisContent);
  genesisBlock.write();
  updateHead(genesisBlock.hash);
  return genesisBlock.hash;
}

/**
 * 获取区块内容
 * @param {string} hash 区块hash
 * @returns 区块Buffer
 */
export function getBlock(hash) {
  const path = `${BLOCKS_PATH}/${hash}`;
  return existsSync(path) ? readFileSync(path) : null;
}

/**
 * 验证区块是否有效
 * @param {Buffer} blockContent 区块内容
 * @param {string} hash 区块hash
 * @returns 验证结果
 */
export function verifyBlock(blockContent, hash) {
  if (!blockContent || !hash) return false
  return getHash(blockContent) === hash;
}

/**
 * 验证区块链是否有效
 * @param {string} genesisHash 创世区块hash
 * @param {(block: Block) => void} callbackOnBlock 区块回调
 * @returns
 */
export function verifyChain(genesisHash, callbackOnBlock) {
  let hash = readFileSync(HEAD_PATH).toString();
  let blockContent = getBlock(hash);
  while (verifyBlock(blockContent, hash)) {
    const block = Block.decrypt(blockContent);
    if (typeof callbackOnBlock === 'function') {
      callbackOnBlock(block);
    }
    if (hash === genesisHash || block.prevHash === genesisHash) {
      return true;
    }
    hash = block.prevHash;
    blockContent = getBlock(hash);
  }
  return false;
}

/**
 * 添加新区块
 * @param {string} genesisHash 创世区块hash
 * @param {Buffer} content 区块内容
 * @returns 添加结果
 */
export function addBlock(genesisHash, content) {
  if (verifyChain(genesisHash)) {
    const prevHash = readFileSync(HEAD_PATH).toString();
    const block = new Block(content, prevHash);
    block.write();
    updateHead(block.hash);
    return block;
  }
  return false;
}

/**
 * 更新头部区块
 * @param {string} hash hash
 */
export function updateHead(hash) {
  writeFileSync(HEAD_PATH, hash);
}

/**
 * 修改区块信息
 * @param {Block} block 区块
 * @param {Buffer} content 区块新内容
 */
export function changeBlock(block, content) {
  block.delete()
  block.data = content;
  block.write()
}
